// Copyright Epic Games, Inc. All Rights Reserved.

#include "AbilitySystem/Phases/LyraGamePhaseSubsystem.h" // Lyra游戏阶段子系统头文件

#include "AbilitySystem/LyraAbilitySystemComponent.h" // Lyra能力系统组件
#include "Engine/World.h" // 世界引擎
#include "GameFramework/GameStateBase.h" // 游戏状态基类
#include "LyraGamePhaseAbility.h" // Lyra游戏阶段能力
#include "LyraGamePhaseLog.h" // Lyra游戏阶段日志

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraGamePhaseSubsystem) // 包含自动生成的代码

class ULyraGameplayAbility; // 前向声明
class UObject; // 前向声明

DEFINE_LOG_CATEGORY(LogLyraGamePhase); // 定义日志类别

//////////////////////////////////////////////////////////////////////
// ULyraGamePhaseSubsystem

/**
 * 构造函数。
 */
ULyraGamePhaseSubsystem::ULyraGamePhaseSubsystem()
{
}

/**
 * 后初始化函数，在子系统创建后调用。
 */
void ULyraGamePhaseSubsystem::PostInitialize()
{
	Super::PostInitialize(); // 调用父类的后初始化
}

/**
 * 判断是否应该为给定的外部对象创建此子系统。
 * @param Outer 外部对象。
 * @return 如果应该创建则返回true，否则返回false。
 */
bool ULyraGamePhaseSubsystem::ShouldCreateSubsystem(UObject* Outer) const
{
	if (Super::ShouldCreateSubsystem(Outer)) // 如果父类判断应该创建
	{
		//UWorld* World = Cast<UWorld>(Outer);
		//check(World);
		//return World->GetAuthGameMode() != nullptr;
		//return nullptr;
		return true; // 返回true，表示应该创建
	}

	return false; // 返回false，表示不应该创建
}

/**
 * 判断子系统是否支持指定的世界类型。
 * @param WorldType 世界类型。
 * @return 如果支持则返回true，否则返回false。
 */
bool ULyraGamePhaseSubsystem::DoesSupportWorldType(const EWorldType::Type WorldType) const
{
	return WorldType == EWorldType::Game || WorldType == EWorldType::PIE; // 支持游戏世界类型和PIE世界类型
}

/**
 * 启动一个游戏阶段。
 * @param PhaseAbility 阶段能力类。
 * @param PhaseEndedCallback 阶段结束时的回调委托。
 */
void ULyraGamePhaseSubsystem::StartPhase(TSubclassOf<ULyraGamePhaseAbility> PhaseAbility, FLyraGamePhaseDelegate PhaseEndedCallback)
{
	UWorld* World = GetWorld(); // 获取世界对象
	ULyraAbilitySystemComponent* GameState_ASC = World->GetGameState()->FindComponentByClass<ULyraAbilitySystemComponent>(); // 从游戏状态获取能力系统组件
	if (ensure(GameState_ASC)) // 确保能力系统组件有效
	{
		FGameplayAbilitySpec PhaseSpec(PhaseAbility, 1, 0, this); // 创建能力规格
		FGameplayAbilitySpecHandle SpecHandle = GameState_ASC->GiveAbilityAndActivateOnce(PhaseSpec); // 授予并激活能力一次
		FGameplayAbilitySpec* FoundSpec = GameState_ASC->FindAbilitySpecFromHandle(SpecHandle); // 查找能力规格
		
		if (FoundSpec && FoundSpec->IsActive()) // 如果找到规格且能力是活跃的
		{
			FLyraGamePhaseEntry& Entry = ActivePhaseMap.FindOrAdd(SpecHandle); // 在活跃阶段映射中找到或添加条目
			Entry.PhaseEndedCallback = PhaseEndedCallback; // 设置阶段结束回调
		}
		else // 如果能力未成功激活
		{
			PhaseEndedCallback.ExecuteIfBound(nullptr); // 执行阶段结束回调，传入空指针
		}
	}
}

/**
 * 蓝图可调用的启动阶段函数。
 * @param PhaseAbility 阶段能力类。
 * @param PhaseEndedDelegate 阶段结束时的动态委托。
 */
void ULyraGamePhaseSubsystem::K2_StartPhase(TSubclassOf<ULyraGamePhaseAbility> PhaseAbility, const FLyraGamePhaseDynamicDelegate& PhaseEndedDelegate)
{
	const FLyraGamePhaseDelegate EndedDelegate = FLyraGamePhaseDelegate::CreateWeakLambda(const_cast<UObject*>(PhaseEndedDelegate.GetUObject()), [PhaseEndedDelegate](const ULyraGamePhaseAbility* PhaseAbility) { // 创建弱引用Lambda委托
		PhaseEndedDelegate.ExecuteIfBound(PhaseAbility); // 执行动态委托
	});

	StartPhase(PhaseAbility, EndedDelegate); // 调用StartPhase函数
}

/**
 * 蓝图可调用的注册阶段开始观察者函数。
 * @param PhaseTag 要观察的阶段标签。
 * @param MatchType 标签匹配类型。
 * @param WhenPhaseActive 阶段活跃时的动态委托。
 */
void ULyraGamePhaseSubsystem::K2_WhenPhaseStartsOrIsActive(FGameplayTag PhaseTag, EPhaseTagMatchType MatchType, FLyraGamePhaseTagDynamicDelegate WhenPhaseActive)
{
	const FLyraGamePhaseTagDelegate ActiveDelegate = FLyraGamePhaseTagDelegate::CreateWeakLambda(WhenPhaseActive.GetUObject(), [WhenPhaseActive](const FGameplayTag& PhaseTag) { // 创建弱引用Lambda委托
		WhenPhaseActive.ExecuteIfBound(PhaseTag); // 执行动态委托
	});

	WhenPhaseStartsOrIsActive(PhaseTag, MatchType, ActiveDelegate); // 调用WhenPhaseStartsOrIsActive函数
}

/**
 * 蓝图可调用的注册阶段结束观察者函数。
 * @param PhaseTag 要观察的阶段标签。
 * @param MatchType 标签匹配类型。
 * @param WhenPhaseEnd 阶段结束时的动态委托。
 */
void ULyraGamePhaseSubsystem::K2_WhenPhaseEnds(FGameplayTag PhaseTag, EPhaseTagMatchType MatchType, FLyraGamePhaseTagDynamicDelegate WhenPhaseEnd)
{
	const FLyraGamePhaseTagDelegate EndedDelegate = FLyraGamePhaseTagDelegate::CreateWeakLambda(WhenPhaseEnd.GetUObject(), [WhenPhaseEnd](const FGameplayTag& PhaseTag) { // 创建弱引用Lambda委托
		WhenPhaseEnd.ExecuteIfBound(PhaseTag); // 执行动态委托
	});

	WhenPhaseEnds(PhaseTag, MatchType, EndedDelegate); // 调用WhenPhaseEnds函数
}

/**
 * 注册一个观察者，当指定阶段开始或已经活跃时触发回调。
 * @param PhaseTag 要观察的阶段标签。
 * @param MatchType 标签匹配类型。
 * @param WhenPhaseActive 阶段活跃时的回调委托。
 */
void ULyraGamePhaseSubsystem::WhenPhaseStartsOrIsActive(FGameplayTag PhaseTag, EPhaseTagMatchType MatchType, const FLyraGamePhaseTagDelegate& WhenPhaseActive)
{
	FPhaseObserver Observer; // 创建观察者
	Observer.PhaseTag = PhaseTag; // 设置阶段标签
	Observer.MatchType = MatchType; // 设置匹配类型
	Observer.PhaseCallback = WhenPhaseActive; // 设置回调委托
	PhaseStartObservers.Add(Observer); // 添加到阶段开始观察者数组

	if (IsPhaseActive(PhaseTag)) // 如果阶段已经活跃
	{
		WhenPhaseActive.ExecuteIfBound(PhaseTag); // 立即执行回调
	}
}

/**
 * 注册一个观察者，当指定阶段结束时触发回调。
 * @param PhaseTag 要观察的阶段标签。
 * @param MatchType 标签匹配类型。
 * @param WhenPhaseEnd 阶段结束时的回调委托。
 */
void ULyraGamePhaseSubsystem::WhenPhaseEnds(FGameplayTag PhaseTag, EPhaseTagMatchType MatchType, const FLyraGamePhaseTagDelegate& WhenPhaseEnd)
{
	FPhaseObserver Observer; // 创建观察者
	Observer.PhaseTag = PhaseTag; // 设置阶段标签
	Observer.MatchType = MatchType; // 设置匹配类型
	Observer.PhaseCallback = WhenPhaseEnd; // 设置回调委托
	PhaseEndObservers.Add(Observer); // 添加到阶段结束观察者数组
}

/**
 * 检查指定阶段标签是否当前处于活跃状态。
 * @param PhaseTag 要检查的阶段标签。
 * @return 如果阶段活跃则返回true，否则返回false。
 */
bool ULyraGamePhaseSubsystem::IsPhaseActive(const FGameplayTag& PhaseTag) const
{
	for (const auto& KVP : ActivePhaseMap) // 遍历活跃阶段映射
	{
		const FLyraGamePhaseEntry& PhaseEntry = KVP.Value; // 获取阶段条目
		if (PhaseEntry.PhaseTag.MatchesTag(PhaseTag)) // 如果阶段标签匹配
		{
			return true; // 返回true
		}
	}

	return false; // 返回false
}

/**
 * 当阶段开始时由阶段能力调用。
 * @param PhaseAbility 开始阶段的阶段能力对象。
 * @param PhaseAbilityHandle 阶段能力的规格句柄。
 */
void ULyraGamePhaseSubsystem::OnBeginPhase(const ULyraGamePhaseAbility* PhaseAbility, const FGameplayAbilitySpecHandle PhaseAbilityHandle)
{
	const FGameplayTag IncomingPhaseTag = PhaseAbility->GetGamePhaseTag(); // 获取传入的阶段标签

	UE_LOG(LogLyraGamePhase, Log, TEXT("Beginning Phase '%s' (%s)"), *IncomingPhaseTag.ToString(), *GetNameSafe(PhaseAbility)); // 记录日志

	const UWorld* World = GetWorld(); // 获取世界对象
	ULyraAbilitySystemComponent* GameState_ASC = World->GetGameState()->FindComponentByClass<ULyraAbilitySystemComponent>(); // 从游戏状态获取能力系统组件
	if (ensure(GameState_ASC)) // 确保能力系统组件有效
	{
		TArray<FGameplayAbilitySpec*> ActivePhases; // 活跃阶段数组
		for (const auto& KVP : ActivePhaseMap) // 遍历活跃阶段映射
		{
			const FGameplayAbilitySpecHandle ActiveAbilityHandle = KVP.Key; // 获取活跃能力句柄
			if (FGameplayAbilitySpec* Spec = GameState_ASC->FindAbilitySpecFromHandle(ActiveAbilityHandle)) // 查找能力规格
			{
				ActivePhases.Add(Spec); // 添加到活跃阶段数组
			}
		}

		for (const FGameplayAbilitySpec* ActivePhase : ActivePhases) // 遍历活跃阶段数组
		{
			const ULyraGamePhaseAbility* ActivePhaseAbility = CastChecked<ULyraGamePhaseAbility>(ActivePhase->Ability); // 转换为阶段能力
			const FGameplayTag ActivePhaseTag = ActivePhaseAbility->GetGamePhaseTag(); // 获取活跃阶段标签
			
			// 所以如果活跃阶段当前匹配传入的阶段标签，我们允许它。
			// 即，多个游戏能力都可以与同一个阶段标签关联。
			// 例如，
			// 你可以处于 Game.Playing 阶段，然后启动一个子阶段，比如 Game.Playing.SuddenDeath
			// Game.Playing 阶段将保持活跃，如果有人要推送另一个，比如，
			// Game.Playing.ActualSuddenDeath，它将结束 Game.Playing.SuddenDeath 阶段，但 Game.Playing 将
			// 继续。类似地，如果我们激活 Game.GameOver，所有 Game.Playing* 阶段将结束。
			if (!IncomingPhaseTag.MatchesTag(ActivePhaseTag)) // 如果传入阶段标签不匹配活跃阶段标签
			{
				UE_LOG(LogLyraGamePhase, Log, TEXT("\tEnding Phase '%s' (%s)"), *ActivePhaseTag.ToString(), *GetNameSafe(ActivePhaseAbility)); // 记录日志

				FGameplayAbilitySpecHandle HandleToEnd = ActivePhase->Handle; // 获取要结束的能力句柄
				GameState_ASC->CancelAbilitiesByFunc([HandleToEnd](const ULyraGameplayAbility* LyraAbility, FGameplayAbilitySpecHandle Handle) { // 取消能力
					return Handle == HandleToEnd; // 如果句柄匹配则取消
				}, true); // 取消能力
			}
		}

		FLyraGamePhaseEntry& Entry = ActivePhaseMap.FindOrAdd(PhaseAbilityHandle); // 在活跃阶段映射中找到或添加条目
		Entry.PhaseTag = IncomingPhaseTag; // 设置阶段标签

		// 通知所有观察此阶段的观察者它已经开始了。
		for (const FPhaseObserver& Observer : PhaseStartObservers) // 遍历阶段开始观察者
		{
			if (Observer.IsMatch(IncomingPhaseTag)) // 如果观察者匹配传入阶段标签
			{
				Observer.PhaseCallback.ExecuteIfBound(IncomingPhaseTag); // 执行回调
			}
		}
	}
}

/**
 * 当阶段结束时由阶段能力调用。
 * @param PhaseAbility 结束阶段的阶段能力对象。
 * @param PhaseAbilityHandle 阶段能力的规格句柄。
 */
void ULyraGamePhaseSubsystem::OnEndPhase(const ULyraGamePhaseAbility* PhaseAbility, const FGameplayAbilitySpecHandle PhaseAbilityHandle)
{
	const FGameplayTag EndedPhaseTag = PhaseAbility->GetGamePhaseTag(); // 获取结束的阶段标签
	UE_LOG(LogLyraGamePhase, Log, TEXT("Ended Phase '%s' (%s)"), *EndedPhaseTag.ToString(), *GetNameSafe(PhaseAbility)); // 记录日志

	const FLyraGamePhaseEntry& Entry = ActivePhaseMap.FindChecked(PhaseAbilityHandle); // 查找阶段条目
	Entry.PhaseEndedCallback.ExecuteIfBound(PhaseAbility); // 执行阶段结束回调

	ActivePhaseMap.Remove(PhaseAbilityHandle); // 从活跃阶段映射中移除

	// 通知所有观察此阶段的观察者它已经结束了。
	for (const FPhaseObserver& Observer : PhaseEndObservers) // 遍历阶段结束观察者
	{
		if (Observer.IsMatch(EndedPhaseTag)) // 如果观察者匹配结束阶段标签
		{
			Observer.PhaseCallback.ExecuteIfBound(EndedPhaseTag); // 执行回调
		}
	}
}

/**
 * 判断观察者是否与给定的阶段标签匹配。
 * @param ComparePhaseTag 要比较的阶段标签。
 * @return 如果匹配则返回true，否则返回false。
 */
bool ULyraGamePhaseSubsystem::FPhaseObserver::IsMatch(const FGameplayTag& ComparePhaseTag) const
{
	switch(MatchType) // 根据匹配类型判断
	{
	case EPhaseTagMatchType::ExactMatch: // 精确匹配
		return ComparePhaseTag == PhaseTag; // 返回是否相等
	case EPhaseTagMatchType::PartialMatch: // 部分匹配
		return ComparePhaseTag.MatchesTag(PhaseTag); // 返回是否匹配标签
	}

	return false; // 默认返回false
}